import java.io.*;
import java.net.*;
import java.util.*;

public class P2P {
	private Socket con;
	private Mensagem msg;
	static int porta;
	private Thread thRequisicao;
	private final String filePath = "/opt/grupo02/";
	public static Controle controle = new Controle();
	private String mestre = null;
	
	public P2P(int port)
	{
		porta = port;
		msg = new Mensagem();
	}
	
	public void fechaConexao() throws IOException
	{
		msg.fechaSreams();
		con.close();
		if (thRequisicao != null)
			thRequisicao.interrupt();
	}
	
	public static void main(String [] args) throws Exception
	{
		int porta = Integer.parseInt(args[1]);
		P2P no = new P2P(porta);
		
		if (args[0].contentEquals("0")) {
			no.mestre();
		}
		else {
			no.mestre = args[2];
			no.abreConexao(no.mestre);
			no.abreStreams();
			no.esperaRequisicao();
			
			for (String arquivo : no.listaArquivos()) {
				no.envia("preadd/" + arquivo + "/" + InetAddress.getLocalHost().getHostName());
				no.recebe();
			}
			
			String opcao = "", str = "", strEnvia = "", nomeArquivo = "";
			BufferedReader terminal = new BufferedReader(new InputStreamReader(System.in));
			
			no.printMenu();
			
			while ((opcao = terminal.readLine()) != null && !opcao.contentEquals("6")) {
				no.clearScreen();
				
				switch (opcao) {
					case "1":
						strEnvia = "add/";
						System.out.print("Arquivo para adicionar: ");
						break;
					case "2":
						strEnvia = "read/";
						System.out.print("Arquivo para ler: ");
						break;
					case "3":
						strEnvia = "del/";
						System.out.print("Arquivo para apagar: ");
						break;
					case "4":
						strEnvia = "move/";
						System.out.print("Arquivo para mover: ");
						break;
					case "5":
						strEnvia = "change/";
						System.out.print("Arquivo para alterar: ");
						break;
					default:
						no.printMenu();
						continue;
				}
				
				nomeArquivo = terminal.readLine();
				if (!no.temArquivo(nomeArquivo) && (opcao.contentEquals("4") || opcao.contentEquals("5")))
					continue;
				
				strEnvia += nomeArquivo;
				strEnvia += "/" + InetAddress.getLocalHost().getHostName();
				
				P2P outroNo = null;
				
				long totalTime = System.nanoTime();
				no.envia(strEnvia);
				
				switch (opcao) {
					case "1": //Adicionar
					case "2": //Ler
						System.out.println("esperando mestre...");
						str = no.recebe();
						System.out.println("recebeu host");
						
						if (str != null) {
							if (str.contentEquals("ja existe")) {
								System.out.println("O arquivo ja existe.");
							}
							else {
								outroNo = new P2P(porta);
								outroNo.abreConexao(str);
								outroNo.abreStreams();
								
								outroNo.envia(strEnvia);
								if (opcao.contentEquals("2"))
									outroNo.recebeArquivo();
								str = outroNo.recebe();
								outroNo.fechaConexao();
								outroNo = null;
							}
						}
						
						no.envia("cabei");
						break;
					case "3": //Apagar
						while ((str = no.recebe()) != null && !str.contentEquals("null")) {
							outroNo = new P2P(porta);
							outroNo.abreConexao(str);
							outroNo.abreStreams();
							
							outroNo.envia(strEnvia);
							str = outroNo.recebe();
							outroNo.fechaConexao();
							outroNo = null;
							
							no.envia("cabei");
							no.envia(strEnvia);
						}
						break;
					case "4": //Mover
						str = no.recebe();
						
						if (str != null) {
							outroNo = new P2P(porta);
							outroNo.abreConexao(str);
							outroNo.abreStreams();
							
							outroNo.envia(strEnvia);
							System.out.println("aqui");
							if (outroNo.enviaArquivo(nomeArquivo)) {
								System.out.println("aqui2");
								str = outroNo.recebe();
								System.out.println("aqui3");
								no.deletaArquivo(nomeArquivo);
							}
						}
						
						no.envia("cabei");
						break;
					case "5": //Alterar
						no.recebe();
						no.alteraArquivo(nomeArquivo);
						no.envia("alterado");
						break;
				}
				
				long estimatedTotal = System.nanoTime() - totalTime;
				
				no.printMenu();
				
				System.out.println("Tempo Total: "+ estimatedTotal);
			}
			
			no.envia("falooous");
			System.out.println("falooous");
			no.fechaConexao();
		}
	}
	
	public void alteraArquivo(String nome)
	{
		PrintWriter arq = null;
		try {
			arq = new PrintWriter(new BufferedWriter(new FileWriter(filePath + nome, true)));
			arq.append("a");
		} catch (Exception e) {
			e.printStackTrace();
		}
		arq.close();
	}
	
	public void deletaArquivo(String nome)
	{
		File arquivo = new File(filePath + nome);
		arquivo.delete();
	}
	
	public Boolean temArquivo(String nome)
	{
		try {
			return listaArquivos().contains(nome);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public List<String> listaArquivos() throws IOException
	{
		ArrayList<String> aux = new ArrayList<String>();
		String separador = java.io.File.separator; 
		
		File folder = new File(separador + "opt" + separador  + "grupo02");
		File[] listOfFiles = folder.listFiles();
		
		for (File f : listOfFiles)
			aux.add(f.getName());
		
		return aux;
	}
	
	public void clearScreen()
	{
		System.out.print("\u001b[2J");
		System.out.flush();
	}
	
	public void printMenu()
	{
		System.out.print("\u001b[2J");
		System.out.println("1 - Criar um arquivo");
		System.out.println("2 - Ler um arquivo");
		System.out.println("3 - Remover um arquivo");
		System.out.println("4 - Mover um arquivo");
		System.out.println("5 - Alterar um arquivo");
		System.out.println("6 - Sair");
	}
	
	public void envia(String texto) throws Exception
	{
		msg.envia(texto);
	}
	
	public String recebe() throws Exception
	{
		return msg.recebe();
	}
	
	public void abreStreams() throws IOException
	{
		msg.abreStreams();
	}
	
	public void abreConexao(String host) throws UnknownHostException, IOException
	{
		con = new Socket(host, porta);
		msg.setSocket(con);
	}
	
	public void aguardaConexao() throws IOException 
	{
		ServerSocket server = new ServerSocket(porta);
		con = server.accept();
		server.close();
	}
	
	public void mestre() throws IOException
	{
		ServerSocket server = new ServerSocket(porta);
		while (!server.isClosed()) {
			P2PThread thread = new P2PThread(server.accept());
			thread.start();
		}
		server.close();
	}
	
	public void esperaRequisicao()
	{
		thRequisicao = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ServerSocket server = new ServerSocket(porta);
					while (!server.isClosed()) {
						new Requisicao(server.accept(), mestre).start();
					}
					server.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		
		thRequisicao.start();
	}
	
	public Boolean enviaArquivo(String path)
	{
		try {
			return msg.enviaArquivo(path);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public void recebeArquivo()
	{
		try {
			msg.recebeArquivo();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
